Skip to content

Conversation

@LFDanLu
Copy link
Member

@LFDanLu LFDanLu commented Oct 13, 2025

Closes

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@rspbot
Copy link

rspbot commented Oct 13, 2025

Comment on lines 17 to 26
// TODO: had to export these for the docs, but not sure why I didn't have to do
// so for the v3 docs?
export {ComboBoxTester} from './combobox';
export {GridListTester} from './gridlist';
export {ListBoxTester} from './listbox';
export {MenuTester} from './menu';
export {SelectTester} from './select';
export {TableTester} from './table';
export {TabsTester} from './tabs';
export {TreeTester} from './tree';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why but something like import comboboxUtils from 'docs:@react-aria/test-utils/src/combobox.ts'; works in the old RAC docs but I had to do the above to get access to the tester types in the new docs

Comment on lines +139 to +140
TODO can't place this next to the header here
<VersionBadge version="beta" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to investigate why, but it seems to break here, probably since it is trying to create an anchor id on a header that has non string content

Comment on lines 185 to 187
### Patterns

<ExampleSwitcher type={null} examples={['ComboBox', 'GridList', 'ListBox', 'Menu', 'Select', 'Table', 'Tabs', 'Tree']}>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In an effort to cut down on bloating the component specific pages, I've tried including the sample code and associated class interface docs in an example switcher. Probably won't scale the best when we add more patterns but open to opinions here

@rspbot
Copy link

rspbot commented Oct 13, 2025

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pared down the content in the page a bit compared to whats on the old docs, feel free to comment on if it feels like too much or not. I kinda wanna add a FAQ/tips part in the test utils section to address some of the common questions that I've seen in the past (what to do if a certain interaction doesn't work, how to handle flows that aren't 100% covered, etc) but it might be too much...

I also remember we discussed in the past that we might like to have one page since these testing/test util practices aren't necessarily specific to a certain implementation. If that is the case, I can flesh this out with some of the RSP/S2 specific points and introduce the FAQ I mentioned above but for now I'll add the S2 equivalent page as well

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added FAQ to draft subpage

@rspbot
Copy link

rspbot commented Oct 13, 2025

@rspbot
Copy link

rspbot commented Oct 21, 2025

Comment on lines +723 to +729


## Testing

TODO make this a subpage


Copy link
Member Author

@LFDanLu LFDanLu Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is what I imagine the content of the subpage would look like if we want to go that route (I suppose it would need to link out to a separate page then? Do we just want it to appear ONLY in the ToC and then link to the page when clicked? Or should there still be a link in the actual docs body content?) The content is roughly standalone, except linking to the testing page for general stuff like handling timers/long press. Open to opinions on what should live in the sub page vs the general testing page OR if one or the other shouldn't exist in favor of the other.

Table is representative of the more lengthy testing sub pages that may exist, for a component that doesn't have a test util tied to it we'd probably only have General setup or simply just link back to the main testing page if at all. Open to opinions for this as well

<ClassAPI links={testUtilDocs.links} class={testUtilDocs.exports.User} />
<ClassAPI links={testUtilDocs.links} class={testUtilDocs.exports.TableTester} />

### Testing FAQ
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit general but I imagine that the sub page should be mostly self contained for the most part. If the FAQ expands to have more than test util specific stuff, then we can re-evaluate

@rspbot
Copy link

rspbot commented Oct 21, 2025

fireEvent.mouseUp(thumb, {pageX: 50});
```

## React Aria test utils
Copy link
Member Author

@LFDanLu LFDanLu Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this section will only exist if the testing sub page per components DOESN'T exist, I kept it here so people can compare/contrast the experience.

IMO, we would perhaps cut this section and make subpages like what I added to Table

@rspbot
Copy link

rspbot commented Oct 25, 2025

@rspbot
Copy link

rspbot commented Oct 25, 2025

## API Changes

@react-aria/test-utils

/@react-aria/test-utils:CheckboxGroupTester

+CheckboxGroupTester {
+  checkboxes: Array<HTMLElement>
+  checkboxgroup: HTMLElement
+  constructor: (CheckboxGroupTesterOpts) => void
+  findCheckbox: ({
+    checkboxIndexOrText: number | string
+}) => HTMLElement
+  selectedCheckboxes: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleCheckbox: (TriggerCheckboxOptions) => Promise<void>
+}

/@react-aria/test-utils:ComboBoxTester

+ComboBoxTester {
+  close: () => Promise<void>
+  combobox: HTMLElement
+  constructor: (ComboBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  focusedOption: HTMLElement | null
+  listbox: HTMLElement | null
+  open: (ComboBoxOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (ComboBoxSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-aria/test-utils:DialogTester

+DialogTester {
+  close: () => Promise<void>
+  constructor: (DialogTesterOpts) => void
+  dialog: HTMLElement | null
+  open: (DialogOpenOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-aria/test-utils:GridListTester

+GridListTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (GridListTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  gridlist: HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowSelection: (GridListToggleRowOpts) => Promise<void>
+  triggerRowAction: (GridListRowActionOpts) => Promise<void>
+}

/@react-aria/test-utils:ListBoxTester

+ListBoxTester {
+  constructor: (ListBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectedOptions: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleOptionSelection: (ListBoxToggleOptionOpts) => Promise<void>
+  triggerOptionAction: (ListBoxOptionActionOpts) => Promise<void>
+}

/@react-aria/test-utils:MenuTester

+MenuTester {
+  close: () => Promise<void>
+  constructor: (MenuTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  menu: HTMLElement | null
+  open: (MenuOpenOpts) => Promise<void>
+  openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester | null>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (MenuSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  submenuTriggers: Array<HTMLElement>
+  trigger: HTMLElement
+}

/@react-aria/test-utils:RadioGroupTester

+RadioGroupTester {
+  constructor: (RadioGroupTesterOpts) => void
+  findRadio: ({
+    radioIndexOrText: number | string
+}) => HTMLElement
+  radiogroup: HTMLElement
+  radios: Array<HTMLElement>
+  selectedRadio: HTMLElement | null
+  setInteractionType: (UserOpts['interactionType']) => void
+  triggerRadio: (TriggerRadioOptions) => Promise<void>
+}

/@react-aria/test-utils:SelectTester

+SelectTester {
+  close: () => Promise<void>
+  constructor: (SelectTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement | null
+  open: (SelectOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (SelectTriggerOptionOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-aria/test-utils:TableTester

+TableTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  columns: Array<HTMLElement>
+  constructor: (TableTesterOpts) => void
+  findCell: ({
+    text: string
+}) => HTMLElement
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rowGroups: Array<HTMLElement>
+  rowHeaders: Array<HTMLElement>
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  table: HTMLElement
+  toggleRowSelection: (TableToggleRowOpts) => Promise<void>
+  toggleSelectAll: ({
+    interactionType?: UserOpts['interactionType']
+}) => Promise<void>
+  toggleSort: (TableToggleSortOpts) => Promise<void>
+  triggerColumnHeaderAction: (TableColumnHeaderActionOpts) => Promise<void>
+  triggerRowAction: (TableRowActionOpts) => Promise<void>
+}

/@react-aria/test-utils:TabsTester

+TabsTester {
+  activeTabpanel: HTMLElement | null
+  constructor: (TabsTesterOpts) => void
+  findTab: ({
+    tabIndexOrText: number | string
+}) => HTMLElement
+  selectedTab: HTMLElement | null
+  setInteractionType: (UserOpts['interactionType']) => void
+  tablist: HTMLElement
+  tabpanels: Array<HTMLElement>
+  tabs: Array<HTMLElement>
+  triggerTab: (TriggerTabOptions) => Promise<void>
+}

/@react-aria/test-utils:TreeTester

+TreeTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (TreeTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowExpansion: (TreeToggleExpansionOpts) => Promise<void>
+  toggleRowSelection: (TreeToggleRowOpts) => Promise<void>
+  tree: HTMLElement
+  triggerRowAction: (TreeRowActionOpts) => Promise<void>
+}

@react-spectrum/test-utils

/@react-spectrum/test-utils:CheckboxGroupTester

+CheckboxGroupTester {
+  checkboxes: Array<HTMLElement>
+  checkboxgroup: HTMLElement
+  constructor: (CheckboxGroupTesterOpts) => void
+  findCheckbox: ({
+    checkboxIndexOrText: number | string
+}) => HTMLElement
+  selectedCheckboxes: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleCheckbox: (TriggerCheckboxOptions) => Promise<void>
+}

/@react-spectrum/test-utils:ComboBoxTester

+ComboBoxTester {
+  close: () => Promise<void>
+  combobox: HTMLElement
+  constructor: (ComboBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  focusedOption: HTMLElement | null
+  listbox: HTMLElement | null
+  open: (ComboBoxOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (ComboBoxSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:DialogTester

+DialogTester {
+  close: () => Promise<void>
+  constructor: (DialogTesterOpts) => void
+  dialog: HTMLElement | null
+  open: (DialogOpenOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:GridListTester

+GridListTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (GridListTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  gridlist: HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowSelection: (GridListToggleRowOpts) => Promise<void>
+  triggerRowAction: (GridListRowActionOpts) => Promise<void>
+}

/@react-spectrum/test-utils:ListBoxTester

+ListBoxTester {
+  constructor: (ListBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectedOptions: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleOptionSelection: (ListBoxToggleOptionOpts) => Promise<void>
+  triggerOptionAction: (ListBoxOptionActionOpts) => Promise<void>
+}

/@react-spectrum/test-utils:MenuTester

+MenuTester {
+  close: () => Promise<void>
+  constructor: (MenuTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  menu: HTMLElement | null
+  open: (MenuOpenOpts) => Promise<void>
+  openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester | null>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (MenuSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  submenuTriggers: Array<HTMLElement>
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:RadioGroupTester

+RadioGroupTester {
+  constructor: (RadioGroupTesterOpts) => void
+  findRadio: ({
+    radioIndexOrText: number | string
+}) => HTMLElement
+  radiogroup: HTMLElement
+  radios: Array<HTMLElement>
+  selectedRadio: HTMLElement | null
+  setInteractionType: (UserOpts['interactionType']) => void
+  triggerRadio: (TriggerRadioOptions) => Promise<void>
+}

/@react-spectrum/test-utils:SelectTester

+SelectTester {
+  close: () => Promise<void>
+  constructor: (SelectTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement | null
+  open: (SelectOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (SelectTriggerOptionOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:TableTester

+TableTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  columns: Array<HTMLElement>
+  constructor: (TableTesterOpts) => void
+  findCell: ({
+    text: string
+}) => HTMLElement
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rowGroups: Array<HTMLElement>
+  rowHeaders: Array<HTMLElement>
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  table: HTMLElement
+  toggleRowSelection: (TableToggleRowOpts) => Promise<void>
+  toggleSelectAll: ({
+    interactionType?: UserOpts['interactionType']
+}) => Promise<void>
+  toggleSort: (TableToggleSortOpts) => Promise<void>
+  triggerColumnHeaderAction: (TableColumnHeaderActionOpts) => Promise<void>
+  triggerRowAction: (TableRowActionOpts) => Promise<void>
+}

/@react-spectrum/test-utils:TabsTester

+TabsTester {
+  activeTabpanel: HTMLElement | null
+  constructor: (TabsTesterOpts) => void
+  findTab: ({
+    tabIndexOrText: number | string
+}) => HTMLElement
+  selectedTab: HTMLElement | null
+  setInteractionType: (UserOpts['interactionType']) => void
+  tablist: HTMLElement
+  tabpanels: Array<HTMLElement>
+  tabs: Array<HTMLElement>
+  triggerTab: (TriggerTabOptions) => Promise<void>
+}

/@react-spectrum/test-utils:TreeTester

+TreeTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (TreeTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowExpansion: (TreeToggleExpansionOpts) => Promise<void>
+  toggleRowSelection: (TreeToggleRowOpts) => Promise<void>
+  tree: HTMLElement
+  triggerRowAction: (TreeRowActionOpts) => Promise<void>
+}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants